Written by Steve Scherrer - July/August 2021

Background

This notebook documents preliminary analysis of tracking data for fish tagged in Molokini Crater between 2020-05-16 and 2021-05-24.

The purpose of this study is to understand how human impacts affect the fish of Molokini Crater

We are particularly interested in answering the following hypotheses: 1. Is the presence of fish affected by vessel presence

  1. Does the proportion of time fish are present within the crater negatively correlated with vessel presence?

Proposed Approach: 1. Begin by calculating the number of each species tagged and basic summary statistics 2. Calculate Metrics - Receiver Use - Pianka’s Niche Overlap - residency 3. Make the following plots - Map - Receiver locations - Map - Average receiver use by Species - Scatterplot - day night plots - Bar Plot - The number of detections per day (individual) - Bar Plot - The number of individuals detected (species) - Line Chart - The proportion of individuals detected n days after tagging (30 day moving average by species) - Bar Plot - Daily vessel traffic - Scatter Plot - vessel traffic vs. proportion of fish detected in crater daily (scatterplot by species) 4. Perform the following statistical Tests - Compare Residency Rates by Species - Compare residency by species, size, and time at liberty - Create a GLM comparing # of individuals in crater regressed against boat traffic and species using AR(1) term on dependent variable on some time scale (daily? 6 hours? depends on resolution of vessel data)

Workspace Setup

Establish Directory Heirarchy

project_directory = '/Users/stephenscherrer/Documents/Programming/Projects/Molokini'
scripts_directory = file.path(project_directory, 'Analysis Scripts')
data_directory = file.path(project_directory, 'Data')
results_directory = file.path(project_directory, 'Results')
figure_directory = file.path(results_directory, 'Figures')

Source package dependencies and utility functions from ‘Utility Functions.R’ file

Load Data

  • load various datafiles
## Files from VUE 
molo_df = load_vemco_data(file.path(data_directory, 'VUE_Export.csv'))
false_detections_df = load_fdf_report(file.path(data_directory, 'FDA.csv'))

## Vessel Traffic data
vessel_df = load_vessel_data(file.path(data_directory, "Molokini_Master_June_21.csv"))

## Metadata Files
tagging_df = load_tagging_data(file.path(data_directory, 'Molokini_Fish_Tagging_master.xlsx'))

# receiver_df = load_receiver_data(file.path(data_directory, ))

Clean Data

  • Associate detections with time of day (day, night, dawn, dusk)
  • Remove detections from tags not associated with this study
  • Remove false detections
## Associate detections with time of day
molo_df = get_time_of_day(molo_df)

## Combine vue df with tagging df - remove irrelevant tags in the process
molo_df = inner_join(x = molo_df, y = tagging_df[ ,c('tag_id', 'species', 'fork_length', 'tagging_date' )], by = 'tag_id')

## Filter false detections
# molo_df = filter_false_detections(molo_df)

## Reclass vessel_df date because R keeps implicitly recasting values...
vessel_df$date = as.POSIXct(as.numeric(vessel_df$date), origin = '1970-01-01', tz = 'HST')

## remove vessel data before start of study
vessel_df = vessel_df[vessel_df$date >= min(molo_df$datetime), ]

Exploratory Data Analysis

Count of individuals tagged by species

## Get count of individuals tagged by species
tags_by_species = aggregate(tag_id ~ species, data = tagging_df, FUN = uniqueN)
  colnames(tags_by_species) = c('species', 'tagged')
  
## Merge with count of individuals detected by species
tags_by_species = left_join(tags_by_species, aggregate(tag_id ~ species, data = molo_df, FUN = uniqueN), by = 'species')

## Replace NA values with 0
tags_by_species[is.na(tags_by_species)] = 0

print(tags_by_species)

Summary Statistics

# Time at liberty
time_at_liberty = calculate_time_at_liberty(molo_df)

# Days Detected
days_detected = calculate_days_detected(molo_df)

# % of days detected
detection_stats = merge(x = days_detected, y = time_at_liberty[ ,c('tag_id', 'days_at_liberty')], on.x = 'tag_id', on.y = 'tag_id')
detection_stats$percent_days_detected = round(detection_stats$unique_days / detection_stats$days_at_liberty, 4) * 100

# Merge with tagging data to get fish info
detection_stats = merge(x = tagging_df[ ,c('tagging_date', 'species', 'tag_id', 'fork_length')], y = detection_stats, on.x = 'tag_id', on.y = 'tag_id')
detection_stats = detection_stats[order(detection_stats$species, detection_stats$tagging_date, detection_stats$tag_id), ]
print(detection_stats)

Metric Calculations

index of receiver use

## sum all spp, sum all individuals (detections of tag at given reciever / all detections of tag)

## Calculate unique detections per tag per receiver station
detections_per_tag_per_receiver = aggregate(datetime~tag_id+receiver+species, data = molo_df, FUN = uniqueN)
colnames(detections_per_tag_per_receiver) = c('tag_id', 'receiver', 'species', 'detections')

## Calculate receiver use metric for each fish and receiver pair
detections_per_tag_per_receiver$receiver_use = 0
for (species in detections_per_tag_per_receiver$species){
  for (i in 1:nrow(detections_per_tag_per_receiver)){
    detections_per_tag_per_receiver$receiver_use[i] = detections_per_tag_per_receiver$detections[i] / sum(detections_per_tag_per_receiver$detections[detections_per_tag_per_receiver$tag_id == detections_per_tag_per_receiver$tag_id[i]])
  }
}

## Calculate average receiver use metric for each tag - Omit stations with no use as this would bias metric
indvidual_receiver_use = aggregate(receiver_use~tag_id+species, data = detections_per_tag_per_receiver[detections_per_tag_per_receiver$receiver_use > 0, ], FUN = mean)

## Add this information to detection_stats
detection_stats = merge(detection_stats, indvidual_receiver_use, on = 'tag_id')

## Calculate receiver use metric by species
species_receiver_use = aggregate(receiver_use~species, data = indvidual_receiver_use, FUN = mean)
colnames(species_receiver_use) = c('species', 'receiver_use')

print(species_receiver_use)

Calculate Pianka’s Niche Overlap Index - Pianka (1973) The Structure of Lizard Communities

0 = no overlap, 1 = perfect overlap

## Aggregate data averaged by species
receiver_use_aggregated_by_species = aggregate(receiver_use ~ species + receiver , data = detections_per_tag_per_receiver, FUN = mean)
  colnames(receiver_use_aggregated_by_species) = c('species', 'receiver', 'avg_use_index')
  
## Reshape from Long to Wide format
receiver_use_aggregated_by_species_wide = dcast(receiver_use_aggregated_by_species, species ~ receiver)
Using avg_use_index as value column: use value.var to override.
## Get all species combinations 
species_combos = data.frame()
for (i in 1:nrow(receiver_use_aggregated_by_species_wide)){
    if(i != nrow(receiver_use_aggregated_by_species_wide)){
    for (j in (i+1):nrow(receiver_use_aggregated_by_species_wide)){
      species_combos = rbind(species_combos, data.frame('species_1' = receiver_use_aggregated_by_species_wide$species[i], 'species_2' = receiver_use_aggregated_by_species_wide$species[j]))
    }
  }
}

## Change any NA values to zero
receiver_use_aggregated_by_species_wide[is.na(receiver_use_aggregated_by_species_wide)] = 0

## Calculate Pianka's index for all pairs
species_combos$pianka_index = 0
for(i in 1:nrow(species_combos)){
  species_combos$pianka_index[i] = sum(receiver_use_aggregated_by_species_wide[receiver_use_aggregated_by_species_wide$species == species_combos$species_1[i], -1] * 
     receiver_use_aggregated_by_species_wide[receiver_use_aggregated_by_species_wide$species == species_combos$species_2[i], -1]) /
    (sqrt(sum(receiver_use_aggregated_by_species_wide[receiver_use_aggregated_by_species_wide$species == species_combos$species_1[i], -1] ^ 2) * 
    sum(receiver_use_aggregated_by_species_wide[receiver_use_aggregated_by_species_wide$species == species_combos$species_2[i], -1] ^ 2)))
}

## Round to 3 digits
species_combos$pianka_index = round(species_combos$pianka_index, 3)

print(species_combos)

Plots

Study Area

## Plot study area and receivers
molo_basemap = get_map(location = c(lon = -156.496331, lat = 20.633007), zoom = 16, maptype = 'satellite')
Source : https://maps.googleapis.com/maps/api/staticmap?center=20.633007,-156.496331&zoom=16&size=640x640&scale=2&maptype=satellite&language=en-EN&key=xxx-hggZe5I57UhGHb8
receiver_map = ggmap(molo_basemap) + geom_point(data = molo_df, mapping = aes(x = lon, y = lat), col = 'red') + labs(x = '°Longitude', y = '°Latitude', title = 'Receiver Locations') + ggsave(filename = 'Receiver Locations Google Map.pdf', path = figure_directory)
Saving 7 x 7 in image
print(receiver_map)

Species Use Plots

## Get average use of receiver by species 
species_receiver_use = aggregate(receiver_use~species+receiver, data = detections_per_tag_per_receiver, FUN = mean)
  colnames(species_receiver_use) = c('species', 'receiver' , 'receiver_use')
  
## Merge with lat lon positions for each receiver from molo_df
receiver_postions =  unique(molo_df[ ,c('receiver', 'lat', 'lon')])
species_receiver_use = merge(x = species_receiver_use, y = receiver_postions, on = 'receiver', all.x = T, all.y = F)

## Make species plots for receiver use by species
for(species in unique(species_receiver_use$species)){
  receiver_use_by_spp = ggmap(molo_basemap) + 
    geom_point(data = species_receiver_use[species_receiver_use$species == species, ], 
               mapping = aes(x = lon, y = lat, color = 'red', size =  receiver_use)) + 
    labs(x = '°Longitude', y = '°Latitude', title = paste('Receiver Use by ', species, sep = '')) +
    ggsave(filename = paste('Receiver Use by ', species, '.pdf', sep = ''), path = figure_directory)
  print(receiver_use_by_spp)
}
Saving 7 x 7 in image

Day Night Plots

### Day Night Plots
## For all fish
pdf(file = file.path(figure_directory, 'Day Night Plot - All Fish.pdf'))
plot_day_night(molo_df, plot_title = 'All Fish')
dev.off()
null device 
          1 
## By Species
for (spp in unique(molo_df$species)){
  pdf(file = file.path(figure_directory, paste('Day Night Plot - Species ', spp, '.pdf', sep = '')))
  plot_day_night(molo_df[molo_df$tag_id == molo_df$tag_id[molo_df$species == spp], ], plot_title = spp)
  dev.off()
}
longer object length is not a multiple of shorter object length
## By Individual
for (tag_id in unique(molo_df$tag_id)){
    pdf(file = file.path(figure_directory, paste('Day Night Plot - Tag ID ', tag_id, '.pdf', sep = '')))
  plot_day_night(molo_df[molo_df$tag_id == tag_id, ], plot_title = paste(tagging_df$species[tagging_df$tag_id == tag_id], '- Tag', as.character(tag_id), sep = ' '))
  dev.off()
}

Barplot of detections by date

### Bar plot of detections in crater by date 
detections_per_day_df = count_detections_per_date(molo_df)

## Barplot of detections by individual
for(i in 1:nrow(detections_per_day_df)){
  ## Convert from wide to long format
  indv_data = melt(detections_per_day_df[i, ])
  colnames(indv_data) = c('date', 'detections')
  
  ## Make and save plot
  ggplot(data = indv_data, mapping = aes(x = date, y = detections)) +
    geom_bar(stat = "identity") + 
    labs(title = paste('Tag ', rownames(detections_per_day_df)[i], sep = ' '), x = 'Date', y = 'Detections') + 
    ggsave(filename = paste('Daily Detection Barplot -', rownames(detections_per_day_df)[i], '.pdf'), path = figure_directory)
}
No id variables; using all as measure variables
Saving 7 x 7 in image
## Detections by species
detections_per_day_spp_stg = detections_per_day_df
detections_per_day_spp_stg$tag_id = rownames(detections_per_day_spp_stg)
detections_per_day_spp_stg = left_join(x = detections_per_day_spp_stg, tagging_df[ ,c('tag_id', 'species')], by = 'tag_id')

## Loop through species
for (spp in unique(detections_per_day_spp_stg$species)){
  
  ## Subset individual df by species
  spp_subset_df = detections_per_day_spp_stg[detections_per_day_spp_stg$species == spp, -which(colnames(detections_per_day_spp_stg) %in% c('tag_id', 'species'))]
  
  ## Convert to long format
  detections_per_spp = melt(colSums(spp_subset_df), value.name =   'detections')
  detections_per_spp$date = rownames(detections_per_spp)
  
  ## Make and save plot
    ggplot(data = detections_per_spp, mapping = aes(x = date, y = detections)) +
    geom_bar(stat = "identity") + 
    labs(title = spp, x = 'Date', y = 'Detections') + 
    ggsave(filename = paste('Daily Detection Barplot -', spp, '.pdf'), path = figure_directory)
}

## Barplot of all detections
all_detections = colSums(detections_per_day_df)
## Convert to long format
all_detections_long = melt(all_detections, value.name =   'detections')
all_detections_long$date = rownames(all_detections_long)
  
## Make and save plot
ggplot(data = all_detections_long, mapping = aes(x = date, y = detections)) +
geom_bar(stat = "identity") + 
labs(title = 'All Tagged Individuals', x = 'Date', y = 'Detections') + 
ggsave(filename = paste('Daily Detection Barplot - all tags.pdf'), path = figure_directory)

Bar plot # of Fish (standardized percent of fish tagged to date) by date and Spp

THIS NEEDS WORK!!!

Bar plot vessel traffic by date

In the future, might also consider max vessels present at a given time

## Calculate Daily Vessel Stats
vessels_per_day = aggregate(vessel_name ~ date, data = vessel_df, FUN = uniqueN)
colnames(vessels_per_day) = c('date', 'daily_vessels')

# Make plot for total 
total_vessels_plot = ggplot(data = vessels_per_day, mapping = aes(x = date, y = daily_vessels)) + 
    geom_bar(stat = 'identity') + 
    labs(title = 'Maximum Number of Co-occuring Vessels Daily', x = 'Date', y = '# of Vessels') +
    ggsave(filename = paste('Total Vessels Daily.pdf ', species, '.pdf', sep = ''), path = figure_directory)
Saving 7 x 7 in image
print(total_vessels_plot)

Scatter plot x axis boat traffic, y axis detections per individual color by spp add error bars for daily detections

## combine vessel and detections per individual
detections_per_day_per_tag = aggregate(datetime ~ date + tag_id + species, data = molo_df, FUN = uniqueN)
colnames(detections_per_day_per_tag) = c('date', 'tag_id', 'species', 'daily_detections')
detection_vessel_counts = left_join(detections_per_day_per_tag, vessels_per_day, by = 'date')

ggplot(data = detection_vessel_counts, mapping = aes(x = daily_vessels, y = daily_detections, color = species)) + geom_point() + ggsave(filename = file.path(figure_directory, 'detections vs vessels.pdf'))
Saving 7 x 7 in image

Model the relationship between detections and vessels (this is ts data so this is just quick and dirty)

Conclusion: There does appear to be a negative trend where increased vessel traffic results in fewer fish. See coefficients. Need to dig deeper if trying to publish these results though, model should be better

Residency and dispersal

## Calculate residency
detection_stats$residence_metric = detection_stats$unique_days / detection_stats$days_at_liberty

## Assign residence category: low = < 33%, medium = 33 - 66, high = >= 66 (Tinhan et al. 2014) -
detection_stats$residence_category = 'Low'
for (i in 1:nrow(detection_stats)){
  if (detection_stats$residence_metric[i] >= (1/3)) {
    detection_stats$residence_category[i] = 'Medium'
  }
  if (detection_stats$residence_metric[i] >= (2/3)) {
    detection_stats$residence_category[i] = 'High'
  }
}

## Create grouped barplot of residency by species
residence_counts_by_species = aggregate(tag_id ~ species + residence_category, data = detection_stats, FUN = length)

ggplot(data = residence_counts_by_species, mapping = aes(x=species, y=tag_id, fill=residence_category)) +
  geom_bar(stat="identity", position = "dodge")

Takeaways - All 4 omilus were highly resident as were grey reef sharks. No other species have replicates so…?

Calculate 30 day moving average of residency, then plot against days since tagging

Statistical analysis

Calculate mean residency by spp (irregardless of time), then ANOVA by spp Use Tukey’s HSD to determine significance

## ANOVA model for residency metric by species
residence_by_species_anova = aov(residence_metric ~ species, data=detection_stats)
summary(residence_by_species_anova)
            Df Sum Sq Mean Sq F value  Pr(>F)   
species      4 0.6781 0.16954   36.28 0.00212 **
Residuals    4 0.0187 0.00467                   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
## Tukey's Honestly Significant Differences between species
TukeyHSD(residence_by_species_anova)
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = residence_metric ~ species, data = detection_stats)

$species
                                           diff        lwr        upr
omilu-grey reef shark                0.13134256 -0.1318229  0.3945080
sandbar shark-grey reef shark       -0.78499266 -1.1571648 -0.4128205
ulua-grey reef shark                -0.04021073 -0.4123829  0.3319614
whitetip reef shark-grey reef shark  0.01604564 -0.3561265  0.3882178
sandbar shark-omilu                 -0.91633521 -1.2560803 -0.5765901
ulua-omilu                          -0.17155329 -0.5112984  0.1681918
whitetip reef shark-omilu           -0.11529692 -0.4550421  0.2244482
ulua-sandbar shark                   0.74478192  0.3150345  1.1745293
whitetip reef shark-sandbar shark    0.80103830  0.3712909  1.2307857
whitetip reef shark-ulua             0.05625637 -0.3734910  0.4860037
                                        p adj
omilu-grey reef shark               0.3282773
sandbar shark-grey reef shark       0.0034142
ulua-grey reef shark                0.9852738
whitetip reef shark-grey reef shark 0.9995567
sandbar shark-omilu                 0.0013197
ulua-omilu                          0.3205721
whitetip reef shark-omilu           0.6064486
ulua-sandbar shark                  0.0071653
whitetip reef shark-sandbar shark   0.0054544
whitetip reef shark-ulua            0.9710009

GLM comparing residency time by spp independent var (time at liberty) dependent (residency index)

summary(species_glm)

Call:
glm(formula = residence_metric ~ species, family = binomial(logit), 
    data = detection_stats)

Deviance Residuals: 
       1         2         3         4         5         6         7  
 0.00007   0.00007   0.00000  -0.26156   0.32896  -0.00022   0.00007  
       8         9  
 0.00000   0.00000  

Coefficients:
                           Estimate Std. Error z value Pr(>|z|)
(Intercept)                  1.8652     2.0751   0.899    0.369
speciesomilu                 4.0251     9.7553   0.413    0.680
speciessandbar shark        -4.2953     4.2135  -1.019    0.308
speciesulua                 -0.3098     3.3547  -0.092    0.926
specieswhitetip reef shark   0.1458     3.7297   0.039    0.969

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 4.31486  on 8  degrees of freedom
Residual deviance: 0.17663  on 4  degrees of freedom
AIC: 11.401

Number of Fisher Scoring iterations: 8

No Significant differences found

GLM comparing time in crater to vessel traffic - Still to come

LS0tCnRpdGxlOiAiTW9sb2tpbmkgQW5hbHlzaXMiCm91dHB1dDoKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCiMgV3JpdHRlbiBieSBTdGV2ZSBTY2hlcnJlciAtIEp1bHkvQXVndXN0IDIwMjEKCiMjIEJhY2tncm91bmQKVGhpcyBub3RlYm9vayBkb2N1bWVudHMgcHJlbGltaW5hcnkgYW5hbHlzaXMgb2YgdHJhY2tpbmcgZGF0YSBmb3IgZmlzaCB0YWdnZWQgaW4gTW9sb2tpbmkgQ3JhdGVyIGJldHdlZW4gMjAyMC0wNS0xNiBhbmQgMjAyMS0wNS0yNC4gCgpUaGUgcHVycG9zZSBvZiB0aGlzIHN0dWR5IGlzIHRvIHVuZGVyc3RhbmQgaG93IGh1bWFuIGltcGFjdHMgYWZmZWN0IHRoZSBmaXNoIG9mIE1vbG9raW5pIENyYXRlcgoKV2UgYXJlIHBhcnRpY3VsYXJseSBpbnRlcmVzdGVkIGluIGFuc3dlcmluZyB0aGUgZm9sbG93aW5nIGh5cG90aGVzZXM6CjEuIElzIHRoZSBwcmVzZW5jZSBvZiBmaXNoIGFmZmVjdGVkIGJ5IHZlc3NlbCBwcmVzZW5jZQoKMi4gRG9lcyB0aGUgcHJvcG9ydGlvbiBvZiB0aW1lIGZpc2ggYXJlIHByZXNlbnQgd2l0aGluIHRoZSBjcmF0ZXIgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHdpdGggdmVzc2VsIHByZXNlbmNlPwoKUHJvcG9zZWQgQXBwcm9hY2g6CjEuIEJlZ2luIGJ5IGNhbGN1bGF0aW5nIHRoZSBudW1iZXIgb2YgZWFjaCBzcGVjaWVzIHRhZ2dlZCBhbmQgYmFzaWMgc3VtbWFyeSBzdGF0aXN0aWNzCjIuIENhbGN1bGF0ZSBNZXRyaWNzCiAgLSBSZWNlaXZlciBVc2UKICAtIFBpYW5rYSdzIE5pY2hlIE92ZXJsYXAKICAtIHJlc2lkZW5jeQozLiBNYWtlIHRoZSBmb2xsb3dpbmcgcGxvdHMKICAtIE1hcCAtIFJlY2VpdmVyIGxvY2F0aW9ucwogIC0gTWFwIC0gQXZlcmFnZSByZWNlaXZlciB1c2UgYnkgU3BlY2llcwogIC0gU2NhdHRlcnBsb3QgLSBkYXkgbmlnaHQgcGxvdHMKICAtIEJhciBQbG90IC0gVGhlIG51bWJlciBvZiBkZXRlY3Rpb25zIHBlciBkYXkgKGluZGl2aWR1YWwpCiAgLSBCYXIgUGxvdCAtIFRoZSBudW1iZXIgb2YgaW5kaXZpZHVhbHMgZGV0ZWN0ZWQgKHNwZWNpZXMpCiAgLSBMaW5lIENoYXJ0IC0gVGhlIHByb3BvcnRpb24gb2YgaW5kaXZpZHVhbHMgZGV0ZWN0ZWQgbiBkYXlzIGFmdGVyIHRhZ2dpbmcgKDMwIGRheSBtb3ZpbmcgYXZlcmFnZSBieSBzcGVjaWVzKQogIC0gQmFyIFBsb3QgLSBEYWlseSB2ZXNzZWwgdHJhZmZpYwogIC0gU2NhdHRlciBQbG90IC0gdmVzc2VsIHRyYWZmaWMgdnMuIHByb3BvcnRpb24gb2YgZmlzaCBkZXRlY3RlZCBpbiBjcmF0ZXIgZGFpbHkgKHNjYXR0ZXJwbG90IGJ5IHNwZWNpZXMpCjQuIFBlcmZvcm0gdGhlIGZvbGxvd2luZyBzdGF0aXN0aWNhbCBUZXN0cwogIC0gQ29tcGFyZSBSZXNpZGVuY3kgUmF0ZXMgYnkgU3BlY2llcwogIC0gQ29tcGFyZSByZXNpZGVuY3kgYnkgc3BlY2llcywgc2l6ZSwgYW5kIHRpbWUgYXQgbGliZXJ0eQogIC0gQ3JlYXRlIGEgR0xNIGNvbXBhcmluZyAjIG9mIGluZGl2aWR1YWxzIGluIGNyYXRlciByZWdyZXNzZWQgYWdhaW5zdCBib2F0IHRyYWZmaWMgYW5kIHNwZWNpZXMgdXNpbmcgQVIoMSkgdGVybSBvbiBkZXBlbmRlbnQgdmFyaWFibGUgb24gc29tZSB0aW1lIHNjYWxlIChkYWlseT8gNiBob3Vycz8gZGVwZW5kcyBvbiByZXNvbHV0aW9uIG9mIHZlc3NlbCBkYXRhKQoKIyBXb3Jrc3BhY2UgU2V0dXAKIyMgRXN0YWJsaXNoIERpcmVjdG9yeSBIZWlyYXJjaHkKYGBge3J9CnByb2plY3RfZGlyZWN0b3J5ID0gJy9Vc2Vycy9zdGVwaGVuc2NoZXJyZXIvRG9jdW1lbnRzL1Byb2dyYW1taW5nL1Byb2plY3RzL01vbG9raW5pJwpzY3JpcHRzX2RpcmVjdG9yeSA9IGZpbGUucGF0aChwcm9qZWN0X2RpcmVjdG9yeSwgJ0FuYWx5c2lzIFNjcmlwdHMnKQpkYXRhX2RpcmVjdG9yeSA9IGZpbGUucGF0aChwcm9qZWN0X2RpcmVjdG9yeSwgJ0RhdGEnKQpyZXN1bHRzX2RpcmVjdG9yeSA9IGZpbGUucGF0aChwcm9qZWN0X2RpcmVjdG9yeSwgJ1Jlc3VsdHMnKQpmaWd1cmVfZGlyZWN0b3J5ID0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyZWN0b3J5LCAnRmlndXJlcycpCmBgYAoKIyMgU291cmNlIHBhY2thZ2UgZGVwZW5kZW5jaWVzIGFuZCB1dGlsaXR5IGZ1bmN0aW9ucyBmcm9tICdVdGlsaXR5IEZ1bmN0aW9ucy5SJyBmaWxlCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnNvdXJjZShmaWxlLnBhdGgoc2NyaXB0c19kaXJlY3RvcnksICdVdGlsaXR5IEZ1bmN0aW9ucy5SJykpCmBgYAoKIyMgTG9hZCBEYXRhCi0gbG9hZCB2YXJpb3VzIGRhdGFmaWxlcyAKYGBge3J9CiMjIEZpbGVzIGZyb20gVlVFIAptb2xvX2RmID0gbG9hZF92ZW1jb19kYXRhKGZpbGUucGF0aChkYXRhX2RpcmVjdG9yeSwgJ1ZVRV9FeHBvcnQuY3N2JykpCmZhbHNlX2RldGVjdGlvbnNfZGYgPSBsb2FkX2ZkZl9yZXBvcnQoZmlsZS5wYXRoKGRhdGFfZGlyZWN0b3J5LCAnRkRBLmNzdicpKQoKIyMgVmVzc2VsIFRyYWZmaWMgZGF0YQp2ZXNzZWxfZGYgPSBsb2FkX3Zlc3NlbF9kYXRhKGZpbGUucGF0aChkYXRhX2RpcmVjdG9yeSwgIk1vbG9raW5pX01hc3Rlcl9KdW5lXzIxLmNzdiIpKQoKIyMgTWV0YWRhdGEgRmlsZXMKdGFnZ2luZ19kZiA9IGxvYWRfdGFnZ2luZ19kYXRhKGZpbGUucGF0aChkYXRhX2RpcmVjdG9yeSwgJ01vbG9raW5pX0Zpc2hfVGFnZ2luZ19tYXN0ZXIueGxzeCcpKQoKIyByZWNlaXZlcl9kZiA9IGxvYWRfcmVjZWl2ZXJfZGF0YShmaWxlLnBhdGgoZGF0YV9kaXJlY3RvcnksICkpCmBgYAoKIyMgQ2xlYW4gRGF0YQotIEFzc29jaWF0ZSBkZXRlY3Rpb25zIHdpdGggdGltZSBvZiBkYXkgKGRheSwgbmlnaHQsIGRhd24sIGR1c2spCi0gUmVtb3ZlIGRldGVjdGlvbnMgZnJvbSB0YWdzIG5vdCBhc3NvY2lhdGVkIHdpdGggdGhpcyBzdHVkeQotIFJlbW92ZSBmYWxzZSBkZXRlY3Rpb25zCmBgYHtyfQojIyBBc3NvY2lhdGUgZGV0ZWN0aW9ucyB3aXRoIHRpbWUgb2YgZGF5Cm1vbG9fZGYgPSBnZXRfdGltZV9vZl9kYXkobW9sb19kZikKCiMjIENvbWJpbmUgdnVlIGRmIHdpdGggdGFnZ2luZyBkZiAtIHJlbW92ZSBpcnJlbGV2YW50IHRhZ3MgaW4gdGhlIHByb2Nlc3MKbW9sb19kZiA9IGlubmVyX2pvaW4oeCA9IG1vbG9fZGYsIHkgPSB0YWdnaW5nX2RmWyAsYygndGFnX2lkJywgJ3NwZWNpZXMnLCAnZm9ya19sZW5ndGgnLCAndGFnZ2luZ19kYXRlJyApXSwgYnkgPSAndGFnX2lkJykKCiMjIEZpbHRlciBmYWxzZSBkZXRlY3Rpb25zCiMgbW9sb19kZiA9IGZpbHRlcl9mYWxzZV9kZXRlY3Rpb25zKG1vbG9fZGYpCgojIyBSZWNsYXNzIHZlc3NlbF9kZiBkYXRlIGJlY2F1c2UgUiBrZWVwcyBpbXBsaWNpdGx5IHJlY2FzdGluZyB2YWx1ZXMuLi4KdmVzc2VsX2RmJGRhdGUgPSBhcy5QT1NJWGN0KGFzLm51bWVyaWModmVzc2VsX2RmJGRhdGUpLCBvcmlnaW4gPSAnMTk3MC0wMS0wMScsIHR6ID0gJ0hTVCcpCgojIyByZW1vdmUgdmVzc2VsIGRhdGEgYmVmb3JlIHN0YXJ0IG9mIHN0dWR5CnZlc3NlbF9kZiA9IHZlc3NlbF9kZlt2ZXNzZWxfZGYkZGF0ZSA+PSBtaW4obW9sb19kZiRkYXRldGltZSksIF0KYGBgCgojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKIyMgQ291bnQgb2YgaW5kaXZpZHVhbHMgdGFnZ2VkIGJ5IHNwZWNpZXMKYGBge3J9CiMjIEdldCBjb3VudCBvZiBpbmRpdmlkdWFscyB0YWdnZWQgYnkgc3BlY2llcwp0YWdzX2J5X3NwZWNpZXMgPSBhZ2dyZWdhdGUodGFnX2lkIH4gc3BlY2llcywgZGF0YSA9IHRhZ2dpbmdfZGYsIEZVTiA9IHVuaXF1ZU4pCiAgY29sbmFtZXModGFnc19ieV9zcGVjaWVzKSA9IGMoJ3NwZWNpZXMnLCAndGFnZ2VkJykKICAKIyMgTWVyZ2Ugd2l0aCBjb3VudCBvZiBpbmRpdmlkdWFscyBkZXRlY3RlZCBieSBzcGVjaWVzCnRhZ3NfYnlfc3BlY2llcyA9IGxlZnRfam9pbih0YWdzX2J5X3NwZWNpZXMsIGFnZ3JlZ2F0ZSh0YWdfaWQgfiBzcGVjaWVzLCBkYXRhID0gbW9sb19kZiwgRlVOID0gdW5pcXVlTiksIGJ5ID0gJ3NwZWNpZXMnKQoKIyMgUmVwbGFjZSBOQSB2YWx1ZXMgd2l0aCAwCnRhZ3NfYnlfc3BlY2llc1tpcy5uYSh0YWdzX2J5X3NwZWNpZXMpXSA9IDAKCnByaW50KHRhZ3NfYnlfc3BlY2llcykKYGBgCgojIyBTdW1tYXJ5IFN0YXRpc3RpY3MKYGBge3J9CiMgVGltZSBhdCBsaWJlcnR5CnRpbWVfYXRfbGliZXJ0eSA9IGNhbGN1bGF0ZV90aW1lX2F0X2xpYmVydHkobW9sb19kZikKCiMgRGF5cyBEZXRlY3RlZApkYXlzX2RldGVjdGVkID0gY2FsY3VsYXRlX2RheXNfZGV0ZWN0ZWQobW9sb19kZikKCiMgJSBvZiBkYXlzIGRldGVjdGVkCmRldGVjdGlvbl9zdGF0cyA9IG1lcmdlKHggPSBkYXlzX2RldGVjdGVkLCB5ID0gdGltZV9hdF9saWJlcnR5WyAsYygndGFnX2lkJywgJ2RheXNfYXRfbGliZXJ0eScpXSwgb24ueCA9ICd0YWdfaWQnLCBvbi55ID0gJ3RhZ19pZCcpCmRldGVjdGlvbl9zdGF0cyRwZXJjZW50X2RheXNfZGV0ZWN0ZWQgPSByb3VuZChkZXRlY3Rpb25fc3RhdHMkdW5pcXVlX2RheXMgLyBkZXRlY3Rpb25fc3RhdHMkZGF5c19hdF9saWJlcnR5LCA0KSAqIDEwMAoKIyBNZXJnZSB3aXRoIHRhZ2dpbmcgZGF0YSB0byBnZXQgZmlzaCBpbmZvCmRldGVjdGlvbl9zdGF0cyA9IG1lcmdlKHggPSB0YWdnaW5nX2RmWyAsYygndGFnZ2luZ19kYXRlJywgJ3NwZWNpZXMnLCAndGFnX2lkJywgJ2ZvcmtfbGVuZ3RoJyldLCB5ID0gZGV0ZWN0aW9uX3N0YXRzLCBvbi54ID0gJ3RhZ19pZCcsIG9uLnkgPSAndGFnX2lkJykKZGV0ZWN0aW9uX3N0YXRzID0gZGV0ZWN0aW9uX3N0YXRzW29yZGVyKGRldGVjdGlvbl9zdGF0cyRzcGVjaWVzLCBkZXRlY3Rpb25fc3RhdHMkdGFnZ2luZ19kYXRlLCBkZXRlY3Rpb25fc3RhdHMkdGFnX2lkKSwgXQpwcmludChkZXRlY3Rpb25fc3RhdHMpCmBgYAoKIyBNZXRyaWMgQ2FsY3VsYXRpb25zCiMjIGluZGV4IG9mIHJlY2VpdmVyIHVzZQpgYGB7cn0KIyMgc3VtIGFsbCBzcHAsIHN1bSBhbGwgaW5kaXZpZHVhbHMgKGRldGVjdGlvbnMgb2YgdGFnIGF0IGdpdmVuIHJlY2lldmVyIC8gYWxsIGRldGVjdGlvbnMgb2YgdGFnKQoKIyMgQ2FsY3VsYXRlIHVuaXF1ZSBkZXRlY3Rpb25zIHBlciB0YWcgcGVyIHJlY2VpdmVyIHN0YXRpb24KZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlciA9IGFnZ3JlZ2F0ZShkYXRldGltZX50YWdfaWQrcmVjZWl2ZXIrc3BlY2llcywgZGF0YSA9IG1vbG9fZGYsIEZVTiA9IHVuaXF1ZU4pCmNvbG5hbWVzKGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIpID0gYygndGFnX2lkJywgJ3JlY2VpdmVyJywgJ3NwZWNpZXMnLCAnZGV0ZWN0aW9ucycpCgojIyBDYWxjdWxhdGUgcmVjZWl2ZXIgdXNlIG1ldHJpYyBmb3IgZWFjaCBmaXNoIGFuZCByZWNlaXZlciBwYWlyCmRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIkcmVjZWl2ZXJfdXNlID0gMApmb3IgKHNwZWNpZXMgaW4gZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlciRzcGVjaWVzKXsKICBmb3IgKGkgaW4gMTpucm93KGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIpKXsKICAgIGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIkcmVjZWl2ZXJfdXNlW2ldID0gZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlciRkZXRlY3Rpb25zW2ldIC8gc3VtKGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIkZGV0ZWN0aW9uc1tkZXRlY3Rpb25zX3Blcl90YWdfcGVyX3JlY2VpdmVyJHRhZ19pZCA9PSBkZXRlY3Rpb25zX3Blcl90YWdfcGVyX3JlY2VpdmVyJHRhZ19pZFtpXV0pCiAgfQp9CgojIyBDYWxjdWxhdGUgYXZlcmFnZSByZWNlaXZlciB1c2UgbWV0cmljIGZvciBlYWNoIHRhZyAtIE9taXQgc3RhdGlvbnMgd2l0aCBubyB1c2UgYXMgdGhpcyB3b3VsZCBiaWFzIG1ldHJpYwppbmR2aWR1YWxfcmVjZWl2ZXJfdXNlID0gYWdncmVnYXRlKHJlY2VpdmVyX3VzZX50YWdfaWQrc3BlY2llcywgZGF0YSA9IGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXJbZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlciRyZWNlaXZlcl91c2UgPiAwLCBdLCBGVU4gPSBtZWFuKQoKIyMgQWRkIHRoaXMgaW5mb3JtYXRpb24gdG8gZGV0ZWN0aW9uX3N0YXRzCmRldGVjdGlvbl9zdGF0cyA9IG1lcmdlKGRldGVjdGlvbl9zdGF0cywgaW5kdmlkdWFsX3JlY2VpdmVyX3VzZSwgb24gPSAndGFnX2lkJykKCiMjIENhbGN1bGF0ZSByZWNlaXZlciB1c2UgbWV0cmljIGJ5IHNwZWNpZXMKc3BlY2llc19yZWNlaXZlcl91c2UgPSBhZ2dyZWdhdGUocmVjZWl2ZXJfdXNlfnNwZWNpZXMsIGRhdGEgPSBpbmR2aWR1YWxfcmVjZWl2ZXJfdXNlLCBGVU4gPSBtZWFuKQpjb2xuYW1lcyhzcGVjaWVzX3JlY2VpdmVyX3VzZSkgPSBjKCdzcGVjaWVzJywgJ3JlY2VpdmVyX3VzZScpCgpwcmludChzcGVjaWVzX3JlY2VpdmVyX3VzZSkKYGBgIAojIyBDYWxjdWxhdGUgUGlhbmthJ3MgTmljaGUgT3ZlcmxhcCBJbmRleCAtIFBpYW5rYSAoMTk3MykgVGhlIFN0cnVjdHVyZSBvZiBMaXphcmQgQ29tbXVuaXRpZXMKIDAgPSBubyBvdmVybGFwLCAxID0gcGVyZmVjdCBvdmVybGFwCmBgYHtyfQojIyBBZ2dyZWdhdGUgZGF0YSBhdmVyYWdlZCBieSBzcGVjaWVzCnJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXMgPSBhZ2dyZWdhdGUocmVjZWl2ZXJfdXNlIH4gc3BlY2llcyArIHJlY2VpdmVyICwgZGF0YSA9IGRldGVjdGlvbnNfcGVyX3RhZ19wZXJfcmVjZWl2ZXIsIEZVTiA9IG1lYW4pCiAgY29sbmFtZXMocmVjZWl2ZXJfdXNlX2FnZ3JlZ2F0ZWRfYnlfc3BlY2llcykgPSBjKCdzcGVjaWVzJywgJ3JlY2VpdmVyJywgJ2F2Z191c2VfaW5kZXgnKQogIAojIyBSZXNoYXBlIGZyb20gTG9uZyB0byBXaWRlIGZvcm1hdApyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUgPSBkY2FzdChyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzLCBzcGVjaWVzIH4gcmVjZWl2ZXIpCgojIyBHZXQgYWxsIHNwZWNpZXMgY29tYmluYXRpb25zIApzcGVjaWVzX2NvbWJvcyA9IGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTpucm93KHJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZSkpewogICAgaWYoaSAhPSBucm93KHJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZSkpewogICAgZm9yIChqIGluIChpKzEpOm5yb3cocmVjZWl2ZXJfdXNlX2FnZ3JlZ2F0ZWRfYnlfc3BlY2llc193aWRlKSl7CiAgICAgIHNwZWNpZXNfY29tYm9zID0gcmJpbmQoc3BlY2llc19jb21ib3MsIGRhdGEuZnJhbWUoJ3NwZWNpZXNfMScgPSByZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUkc3BlY2llc1tpXSwgJ3NwZWNpZXNfMicgPSByZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUkc3BlY2llc1tqXSkpCiAgICB9CiAgfQp9CgojIyBDaGFuZ2UgYW55IE5BIHZhbHVlcyB0byB6ZXJvCnJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZVtpcy5uYShyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUpXSA9IDAKCiMjIENhbGN1bGF0ZSBQaWFua2EncyBpbmRleCBmb3IgYWxsIHBhaXJzCnNwZWNpZXNfY29tYm9zJHBpYW5rYV9pbmRleCA9IDAKZm9yKGkgaW4gMTpucm93KHNwZWNpZXNfY29tYm9zKSl7CiAgc3BlY2llc19jb21ib3MkcGlhbmthX2luZGV4W2ldID0gc3VtKHJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZVtyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUkc3BlY2llcyA9PSBzcGVjaWVzX2NvbWJvcyRzcGVjaWVzXzFbaV0sIC0xXSAqIAogICAgIHJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZVtyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUkc3BlY2llcyA9PSBzcGVjaWVzX2NvbWJvcyRzcGVjaWVzXzJbaV0sIC0xXSkgLwogICAgKHNxcnQoc3VtKHJlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZVtyZWNlaXZlcl91c2VfYWdncmVnYXRlZF9ieV9zcGVjaWVzX3dpZGUkc3BlY2llcyA9PSBzcGVjaWVzX2NvbWJvcyRzcGVjaWVzXzFbaV0sIC0xXSBeIDIpICogCiAgICBzdW0ocmVjZWl2ZXJfdXNlX2FnZ3JlZ2F0ZWRfYnlfc3BlY2llc193aWRlW3JlY2VpdmVyX3VzZV9hZ2dyZWdhdGVkX2J5X3NwZWNpZXNfd2lkZSRzcGVjaWVzID09IHNwZWNpZXNfY29tYm9zJHNwZWNpZXNfMltpXSwgLTFdIF4gMikpKQp9CgojIyBSb3VuZCB0byAzIGRpZ2l0cwpzcGVjaWVzX2NvbWJvcyRwaWFua2FfaW5kZXggPSByb3VuZChzcGVjaWVzX2NvbWJvcyRwaWFua2FfaW5kZXgsIDMpCgpwcmludChzcGVjaWVzX2NvbWJvcykKYGBgCgojIyBQbG90cwpTdHVkeSBBcmVhCmBgYHtyfQojIyBQbG90IHN0dWR5IGFyZWEgYW5kIHJlY2VpdmVycwptb2xvX2Jhc2VtYXAgPSBnZXRfbWFwKGxvY2F0aW9uID0gYyhsb24gPSAtMTU2LjQ5NjMzMSwgbGF0ID0gMjAuNjMzMDA3KSwgem9vbSA9IDE2LCBtYXB0eXBlID0gJ3NhdGVsbGl0ZScpCnJlY2VpdmVyX21hcCA9IGdnbWFwKG1vbG9fYmFzZW1hcCkgKyBnZW9tX3BvaW50KGRhdGEgPSBtb2xvX2RmLCBtYXBwaW5nID0gYWVzKHggPSBsb24sIHkgPSBsYXQpLCBjb2wgPSAncmVkJykgKyBsYWJzKHggPSAnwrBMb25naXR1ZGUnLCB5ID0gJ8KwTGF0aXR1ZGUnLCB0aXRsZSA9ICdSZWNlaXZlciBMb2NhdGlvbnMnKSArIGdnc2F2ZShmaWxlbmFtZSA9ICdSZWNlaXZlciBMb2NhdGlvbnMgR29vZ2xlIE1hcC5wZGYnLCBwYXRoID0gZmlndXJlX2RpcmVjdG9yeSkKcHJpbnQocmVjZWl2ZXJfbWFwKQpgYGAKCiMjIyBTcGVjaWVzIFVzZSBQbG90cwpgYGB7cn0KIyMgR2V0IGF2ZXJhZ2UgdXNlIG9mIHJlY2VpdmVyIGJ5IHNwZWNpZXMgCnNwZWNpZXNfcmVjZWl2ZXJfdXNlID0gYWdncmVnYXRlKHJlY2VpdmVyX3VzZX5zcGVjaWVzK3JlY2VpdmVyLCBkYXRhID0gZGV0ZWN0aW9uc19wZXJfdGFnX3Blcl9yZWNlaXZlciwgRlVOID0gbWVhbikKICBjb2xuYW1lcyhzcGVjaWVzX3JlY2VpdmVyX3VzZSkgPSBjKCdzcGVjaWVzJywgJ3JlY2VpdmVyJyAsICdyZWNlaXZlcl91c2UnKQogIAojIyBNZXJnZSB3aXRoIGxhdCBsb24gcG9zaXRpb25zIGZvciBlYWNoIHJlY2VpdmVyIGZyb20gbW9sb19kZgpyZWNlaXZlcl9wb3N0aW9ucyA9ICB1bmlxdWUobW9sb19kZlsgLGMoJ3JlY2VpdmVyJywgJ2xhdCcsICdsb24nKV0pCnNwZWNpZXNfcmVjZWl2ZXJfdXNlID0gbWVyZ2UoeCA9IHNwZWNpZXNfcmVjZWl2ZXJfdXNlLCB5ID0gcmVjZWl2ZXJfcG9zdGlvbnMsIG9uID0gJ3JlY2VpdmVyJywgYWxsLnggPSBULCBhbGwueSA9IEYpCgojIyBNYWtlIHNwZWNpZXMgcGxvdHMgZm9yIHJlY2VpdmVyIHVzZSBieSBzcGVjaWVzCmZvcihzcGVjaWVzIGluIHVuaXF1ZShzcGVjaWVzX3JlY2VpdmVyX3VzZSRzcGVjaWVzKSl7CiAgcmVjZWl2ZXJfdXNlX2J5X3NwcCA9IGdnbWFwKG1vbG9fYmFzZW1hcCkgKyAKICAgIGdlb21fcG9pbnQoZGF0YSA9IHNwZWNpZXNfcmVjZWl2ZXJfdXNlW3NwZWNpZXNfcmVjZWl2ZXJfdXNlJHNwZWNpZXMgPT0gc3BlY2llcywgXSwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgY29sb3IgPSAncmVkJywgc2l6ZSA9ICByZWNlaXZlcl91c2UpKSArIAogICAgbGFicyh4ID0gJ8KwTG9uZ2l0dWRlJywgeSA9ICfCsExhdGl0dWRlJywgdGl0bGUgPSBwYXN0ZSgnUmVjZWl2ZXIgVXNlIGJ5ICcsIHNwZWNpZXMsIHNlcCA9ICcnKSkgKwogICAgZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUoJ1JlY2VpdmVyIFVzZSBieSAnLCBzcGVjaWVzLCAnLnBkZicsIHNlcCA9ICcnKSwgcGF0aCA9IGZpZ3VyZV9kaXJlY3RvcnkpCiAgcHJpbnQocmVjZWl2ZXJfdXNlX2J5X3NwcCkKfQpgYGAKCiMjIyBEYXkgTmlnaHQgUGxvdHMKYGBge3J9CiMjIyBEYXkgTmlnaHQgUGxvdHMKIyMgRm9yIGFsbCBmaXNoCnBkZihmaWxlID0gZmlsZS5wYXRoKGZpZ3VyZV9kaXJlY3RvcnksICdEYXkgTmlnaHQgUGxvdCAtIEFsbCBGaXNoLnBkZicpKQpwbG90X2RheV9uaWdodChtb2xvX2RmLCBwbG90X3RpdGxlID0gJ0FsbCBGaXNoJykKZGV2Lm9mZigpCgojIyBCeSBTcGVjaWVzCmZvciAoc3BwIGluIHVuaXF1ZShtb2xvX2RmJHNwZWNpZXMpKXsKICBwZGYoZmlsZSA9IGZpbGUucGF0aChmaWd1cmVfZGlyZWN0b3J5LCBwYXN0ZSgnRGF5IE5pZ2h0IFBsb3QgLSBTcGVjaWVzICcsIHNwcCwgJy5wZGYnLCBzZXAgPSAnJykpKQogIHBsb3RfZGF5X25pZ2h0KG1vbG9fZGZbbW9sb19kZiR0YWdfaWQgPT0gbW9sb19kZiR0YWdfaWRbbW9sb19kZiRzcGVjaWVzID09IHNwcF0sIF0sIHBsb3RfdGl0bGUgPSBzcHApCiAgZGV2Lm9mZigpCn0KCiMjIEJ5IEluZGl2aWR1YWwKZm9yICh0YWdfaWQgaW4gdW5pcXVlKG1vbG9fZGYkdGFnX2lkKSl7CiAgICBwZGYoZmlsZSA9IGZpbGUucGF0aChmaWd1cmVfZGlyZWN0b3J5LCBwYXN0ZSgnRGF5IE5pZ2h0IFBsb3QgLSBUYWcgSUQgJywgdGFnX2lkLCAnLnBkZicsIHNlcCA9ICcnKSkpCiAgcGxvdF9kYXlfbmlnaHQobW9sb19kZlttb2xvX2RmJHRhZ19pZCA9PSB0YWdfaWQsIF0sIHBsb3RfdGl0bGUgPSBwYXN0ZSh0YWdnaW5nX2RmJHNwZWNpZXNbdGFnZ2luZ19kZiR0YWdfaWQgPT0gdGFnX2lkXSwgJy0gVGFnJywgYXMuY2hhcmFjdGVyKHRhZ19pZCksIHNlcCA9ICcgJykpCiAgZGV2Lm9mZigpCn0KYGBgCgoKIyMjIEJhcnBsb3Qgb2YgZGV0ZWN0aW9ucyBieSBkYXRlCmBgYHtyfQojIyMgQmFyIHBsb3Qgb2YgZGV0ZWN0aW9ucyBpbiBjcmF0ZXIgYnkgZGF0ZSAKZGV0ZWN0aW9uc19wZXJfZGF5X2RmID0gY291bnRfZGV0ZWN0aW9uc19wZXJfZGF0ZShtb2xvX2RmKQoKIyMgQmFycGxvdCBvZiBkZXRlY3Rpb25zIGJ5IGluZGl2aWR1YWwKZm9yKGkgaW4gMTpucm93KGRldGVjdGlvbnNfcGVyX2RheV9kZikpewogICMjIENvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0CiAgaW5kdl9kYXRhID0gbWVsdChkZXRlY3Rpb25zX3Blcl9kYXlfZGZbaSwgXSkKICBjb2xuYW1lcyhpbmR2X2RhdGEpID0gYygnZGF0ZScsICdkZXRlY3Rpb25zJykKICAKICAjIyBNYWtlIGFuZCBzYXZlIHBsb3QKICBnZ3Bsb3QoZGF0YSA9IGluZHZfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZSwgeSA9IGRldGVjdGlvbnMpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyAKICAgIGxhYnModGl0bGUgPSBwYXN0ZSgnVGFnICcsIHJvd25hbWVzKGRldGVjdGlvbnNfcGVyX2RheV9kZilbaV0sIHNlcCA9ICcgJyksIHggPSAnRGF0ZScsIHkgPSAnRGV0ZWN0aW9ucycpICsgCiAgICBnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZSgnRGFpbHkgRGV0ZWN0aW9uIEJhcnBsb3QgLScsIHJvd25hbWVzKGRldGVjdGlvbnNfcGVyX2RheV9kZilbaV0sICcucGRmJyksIHBhdGggPSBmaWd1cmVfZGlyZWN0b3J5KQp9CgojIyBEZXRlY3Rpb25zIGJ5IHNwZWNpZXMKZGV0ZWN0aW9uc19wZXJfZGF5X3NwcF9zdGcgPSBkZXRlY3Rpb25zX3Blcl9kYXlfZGYKZGV0ZWN0aW9uc19wZXJfZGF5X3NwcF9zdGckdGFnX2lkID0gcm93bmFtZXMoZGV0ZWN0aW9uc19wZXJfZGF5X3NwcF9zdGcpCmRldGVjdGlvbnNfcGVyX2RheV9zcHBfc3RnID0gbGVmdF9qb2luKHggPSBkZXRlY3Rpb25zX3Blcl9kYXlfc3BwX3N0ZywgdGFnZ2luZ19kZlsgLGMoJ3RhZ19pZCcsICdzcGVjaWVzJyldLCBieSA9ICd0YWdfaWQnKQoKIyMgTG9vcCB0aHJvdWdoIHNwZWNpZXMKZm9yIChzcHAgaW4gdW5pcXVlKGRldGVjdGlvbnNfcGVyX2RheV9zcHBfc3RnJHNwZWNpZXMpKXsKICAKICAjIyBTdWJzZXQgaW5kaXZpZHVhbCBkZiBieSBzcGVjaWVzCiAgc3BwX3N1YnNldF9kZiA9IGRldGVjdGlvbnNfcGVyX2RheV9zcHBfc3RnW2RldGVjdGlvbnNfcGVyX2RheV9zcHBfc3RnJHNwZWNpZXMgPT0gc3BwLCAtd2hpY2goY29sbmFtZXMoZGV0ZWN0aW9uc19wZXJfZGF5X3NwcF9zdGcpICVpbiUgYygndGFnX2lkJywgJ3NwZWNpZXMnKSldCiAgCiAgIyMgQ29udmVydCB0byBsb25nIGZvcm1hdAogIGRldGVjdGlvbnNfcGVyX3NwcCA9IG1lbHQoY29sU3VtcyhzcHBfc3Vic2V0X2RmKSwgdmFsdWUubmFtZSA9ICAgJ2RldGVjdGlvbnMnKQogIGRldGVjdGlvbnNfcGVyX3NwcCRkYXRlID0gcm93bmFtZXMoZGV0ZWN0aW9uc19wZXJfc3BwKQogIAogICMjIE1ha2UgYW5kIHNhdmUgcGxvdAogICAgZ2dwbG90KGRhdGEgPSBkZXRlY3Rpb25zX3Blcl9zcHAsIG1hcHBpbmcgPSBhZXMoeCA9IGRhdGUsIHkgPSBkZXRlY3Rpb25zKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgICBsYWJzKHRpdGxlID0gc3BwLCB4ID0gJ0RhdGUnLCB5ID0gJ0RldGVjdGlvbnMnKSArIAogICAgZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUoJ0RhaWx5IERldGVjdGlvbiBCYXJwbG90IC0nLCBzcHAsICcucGRmJyksIHBhdGggPSBmaWd1cmVfZGlyZWN0b3J5KQp9CgojIyBCYXJwbG90IG9mIGFsbCBkZXRlY3Rpb25zCmFsbF9kZXRlY3Rpb25zID0gY29sU3VtcyhkZXRlY3Rpb25zX3Blcl9kYXlfZGYpCiMjIENvbnZlcnQgdG8gbG9uZyBmb3JtYXQKYWxsX2RldGVjdGlvbnNfbG9uZyA9IG1lbHQoYWxsX2RldGVjdGlvbnMsIHZhbHVlLm5hbWUgPSAgICdkZXRlY3Rpb25zJykKYWxsX2RldGVjdGlvbnNfbG9uZyRkYXRlID0gcm93bmFtZXMoYWxsX2RldGVjdGlvbnNfbG9uZykKICAKIyMgTWFrZSBhbmQgc2F2ZSBwbG90CmdncGxvdChkYXRhID0gYWxsX2RldGVjdGlvbnNfbG9uZywgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZSwgeSA9IGRldGVjdGlvbnMpKSArCmdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIApsYWJzKHRpdGxlID0gJ0FsbCBUYWdnZWQgSW5kaXZpZHVhbHMnLCB4ID0gJ0RhdGUnLCB5ID0gJ0RldGVjdGlvbnMnKSArIApnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZSgnRGFpbHkgRGV0ZWN0aW9uIEJhcnBsb3QgLSBhbGwgdGFncy5wZGYnKSwgcGF0aCA9IGZpZ3VyZV9kaXJlY3RvcnkpCmBgYAoKIyMjIEJhciBwbG90ICMgb2YgRmlzaCAoc3RhbmRhcmRpemVkIHBlcmNlbnQgb2YgZmlzaCB0YWdnZWQgdG8gZGF0ZSkgYnkgZGF0ZSBhbmQgU3BwCgoKVEhJUyBORUVEUyBXT1JLISEhCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMjIENvbnZlcnQgZGV0ZWN0aW9uc19wZXJfZGF5IHRvIHByZXNlbmNlL2Fic2VuY2UKcHJlc2VuY2VfYWJzZW5jZV93aWRlX2RmID0gZGV0ZWN0aW9uc19wZXJfZGF5X2RmCnByZXNlbmNlX2Fic2VuY2Vfd2lkZV9kZltwcmVzZW5jZV9hYnNlbmNlX3dpZGVfZGYgPiAwXSA9IDEKCiMjIENvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0CnByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZiA9IG1lbHQocHJlc2VuY2VfYWJzZW5jZV93aWRlX2RmLCBpZC52YXJzID0gYygnZGF0ZScpLCBtZWFzdXJlLnZhcnMgPSBjb2xuYW1lcyhwcmVzZW5jZV9hYnNlbmNlX3dpZGVfZGYpWzI6bmNvbChwcmVzZW5jZV9hYnNlbmNlX3dpZGVfZGYpXSwgdmFyaWFibGUubmFtZSA9ICd0YWdfaWQnLCB2YWx1ZS5uYW1lID0gJ2RldGVjdGVkJykKCiMgRHJvcCAndGFnXycgcHJlZml4IGZyb20gdGFnX2lkIGNvbHVtbiBmb3IgbWF0Y2hpbmcgcHVycG9zZXMKcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmJHRhZ19pZCA9IGxldmVscyhwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYkdGFnX2lkKVtwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYkdGFnX2lkXQpmb3IoaSBpbiAxOm5yb3cocHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmKSl7CiAgcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmJHRhZ19pZFtpXSA9IHN0cnNwbGl0KHByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZiR0YWdfaWRbaV0sIHNwbGl0ID0gJ18nKVtbMV1dWzJdCn0KCiMjIE1lcmdlIHdpdGggc3BlY2llcyBmcm9tIHRhZ2dpbmcgZGF0YQpwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYgPSBtZXJnZSh4ID0gcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmLCB5ID0gdGFnZ2luZ19kZlsgLGMoJ3RhZ19pZCcsICdzcGVjaWVzJyldLCBvbiA9ICd0YWdfaWQnKQoKIyMgRHJvcCBkYXRlIGFuZCB0YWcgcGFpcnMgcHJlY2VkaW5nIHRoZSBkYXRlIHRoZSBmaXNoIHdhcyB0YWdnZWQKaW5kaWNpZXNfdG9fZHJvcCA9IGMoKQpmb3IoaSBpbiBucm93KHByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZikpewogIGlmKGFzLkRhdGUodGFnZ2luZ19kZiRkYXRldGltZVt0YWdnaW5nX2RmJHRhZ19pZCA9PSBwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYkdGFnX2lkW2ldXSkgPD0gcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmJGRhdGVbaV0pewogICAgaW5kaWNpZXNfdG9fZHJvcCA9IGMoaW5kaWNpZXNfdG9fZHJvcCwgaSkKICB9Cn0KcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmID0gcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmWy1pbmRpY2llc190b19kcm9wLCBdCgojIyBHZXQgYSBsaXN0IG9mIGFjdGl2ZSB0YWdzIGJ5IGRhdGUgYW5kIHNwZWNpZXMKYWN0aXZlX3RhZ3NfYnlfZGF0ZSA9IGFnZ3JlZ2F0ZSh0YWdfaWQgfiBkYXRlICsgc3BlY2llcywgZGF0YSA9IHByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZiwgRlVOID0gdW5pcXVlTikKICBjb2xuYW1lcyhhY3RpdmVfdGFnc19ieV9kYXRlKSA9IGMoJ2RhdGUnLCAnc3BlY2llcycsICdkZXBsb3llZF90YWdzJykKCiMjIFN0YW5kYXJkaXplIHRhZyBjb3VudHMgYnkgdGFncyBkZXBsb3llZCBhbmQgcGxvdCBhcyAlIG9mIHRhZ3MgZGV0ZWN0ZWQgcGVyIGRheSBieSBzcGVjaWVzCmZvcihzcGVjaWVzIGluIHVuaXF1ZShwcmVzZW5jZV9hYnNlbmNlX2xvbmdfZGYkc3BlY2llcykpewogIAogICMgQ291bnQgbnVtYmVyIG9mIHRhZ3MgZGV0ZWN0ZWQgZGFpbHkgYnkgc3BlY2llcyAKICBwcmVzZW5jZV9hYnNlbmNlX2J5X3NwcF9kZiA9IGFnZ3JlZ2F0ZShkZXRlY3RlZH5kYXRlLCBkYXRhID0gcHJlc2VuY2VfYWJzZW5jZV9sb25nX2RmW3ByZXNlbmNlX2Fic2VuY2VfbG9uZ19kZiRzcGVjaWVzID09IHNwZWNpZXMsIF0sIEZVTiA9IHN1bSkKICBjb2xuYW1lcyhwcmVzZW5jZV9hYnNlbmNlX2J5X3NwcF9kZikgPSBjKCdkYXRlJywgJ3RhZ3NfZGV0ZWN0ZWQnKQogIAogICMgU3RhbmRhcmRpemUgZGFpbHkgdGFnIGNvdW50IGJ5IHRoZSBudW1iZXIgb2YgdGFncyBkZXBsb3llZAogIHByZXNlbmNlX2Fic2VuY2VfYnlfc3BwX2RmID0gbWVyZ2UoeCA9IHByZXNlbmNlX2Fic2VuY2VfYnlfc3BwX2RmLCB5ID0gYWN0aXZlX3RhZ3NfYnlfZGF0ZVthY3RpdmVfdGFnc19ieV9kYXRlJHNwZWNpZXMgPT0gc3BlY2llcywgXSwgb24gPSAnZGF0ZScpCiAgcHJlc2VuY2VfYWJzZW5jZV9ieV9zcHBfZGYkcGVyY2VudF90YWdzX2RldGVjdGVkID0gcHJlc2VuY2VfYWJzZW5jZV9ieV9zcHBfZGYkZGV0ZWN0ZWQgLyBwcmVzZW5jZV9hYnNlbmNlX2J5X3NwcF9kZiRkZXBsb3llZF90YWdzCiAgCiAgIyBNYWtlIHBsb3QgYXQgc3BlY2llcyBsZXZlbAogIGdncGxvdChkYXRhID0gcHJlc2VuY2VfYWJzZW5jZV9ieV9zcHBfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IGRhdGUsIHkgPSBwZXJjZW50X3RhZ3NfZGV0ZWN0ZWQpKSArIAogICAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsgCiAgICBsYWJzKHRpdGxlID0gc3BlY2llcywgeCA9ICdEYXRlJywgeSA9ICclIG9mIHRhZ3MgZGV0ZWN0ZWQnKSArCiAgICBnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZSgnRGV0ZWN0aW9ucyBTdGFuZGFyZGl6ZWQgQnkgU3BlY2llcyAtICcsIHNwZWNpZXMsICcucGRmJywgc2VwID0gJycpLCBwYXRoID0gZmlndXJlX2RpcmVjdG9yeSkKfQoKYGBgCgojIyMgQmFyIHBsb3QgdmVzc2VsIHRyYWZmaWMgYnkgZGF0ZQpJbiB0aGUgZnV0dXJlLCBtaWdodCBhbHNvIGNvbnNpZGVyIG1heCB2ZXNzZWxzIHByZXNlbnQgYXQgYSBnaXZlbiB0aW1lCmBgYHtyfQojIyBDYWxjdWxhdGUgRGFpbHkgVmVzc2VsIFN0YXRzCnZlc3NlbHNfcGVyX2RheSA9IGFnZ3JlZ2F0ZSh2ZXNzZWxfbmFtZSB+IGRhdGUsIGRhdGEgPSB2ZXNzZWxfZGYsIEZVTiA9IHVuaXF1ZU4pCmNvbG5hbWVzKHZlc3NlbHNfcGVyX2RheSkgPSBjKCdkYXRlJywgJ2RhaWx5X3Zlc3NlbHMnKQoKIyBNYWtlIHBsb3QgZm9yIHRvdGFsIAp0b3RhbF92ZXNzZWxzX3Bsb3QgPSBnZ3Bsb3QoZGF0YSA9IHZlc3NlbHNfcGVyX2RheSwgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZSwgeSA9IGRhaWx5X3Zlc3NlbHMpKSArIAogICAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsgCiAgICBsYWJzKHRpdGxlID0gJ01heGltdW0gTnVtYmVyIG9mIENvLW9jY3VyaW5nIFZlc3NlbHMgRGFpbHknLCB4ID0gJ0RhdGUnLCB5ID0gJyMgb2YgVmVzc2VscycpICsKICAgIGdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlKCdUb3RhbCBWZXNzZWxzIERhaWx5LnBkZiAnLCBzcGVjaWVzLCAnLnBkZicsIHNlcCA9ICcnKSwgcGF0aCA9IGZpZ3VyZV9kaXJlY3RvcnkpCgpwcmludCh0b3RhbF92ZXNzZWxzX3Bsb3QpCmBgYAoKCiMjIyBTY2F0dGVyIHBsb3QgeCBheGlzIGJvYXQgdHJhZmZpYywgeSBheGlzIGRldGVjdGlvbnMgcGVyIGluZGl2aWR1YWwgY29sb3IgYnkgc3BwIGFkZCBlcnJvciBiYXJzIGZvciBkYWlseSBkZXRlY3Rpb25zCmBgYHtyfQojIyBjb21iaW5lIHZlc3NlbCBhbmQgZGV0ZWN0aW9ucyBwZXIgaW5kaXZpZHVhbApkZXRlY3Rpb25zX3Blcl9kYXlfcGVyX3RhZyA9IGFnZ3JlZ2F0ZShkYXRldGltZSB+IGRhdGUgKyB0YWdfaWQgKyBzcGVjaWVzLCBkYXRhID0gbW9sb19kZiwgRlVOID0gdW5pcXVlTikKY29sbmFtZXMoZGV0ZWN0aW9uc19wZXJfZGF5X3Blcl90YWcpID0gYygnZGF0ZScsICd0YWdfaWQnLCAnc3BlY2llcycsICdkYWlseV9kZXRlY3Rpb25zJykKZGV0ZWN0aW9uX3Zlc3NlbF9jb3VudHMgPSBsZWZ0X2pvaW4oZGV0ZWN0aW9uc19wZXJfZGF5X3Blcl90YWcsIHZlc3NlbHNfcGVyX2RheSwgYnkgPSAnZGF0ZScpCgpnZ3Bsb3QoZGF0YSA9IGRldGVjdGlvbl92ZXNzZWxfY291bnRzLCBtYXBwaW5nID0gYWVzKHggPSBkYWlseV92ZXNzZWxzLCB5ID0gZGFpbHlfZGV0ZWN0aW9ucywgY29sb3IgPSBzcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgoZmlndXJlX2RpcmVjdG9yeSwgJ2RldGVjdGlvbnMgdnMgdmVzc2Vscy5wZGYnKSkKYGBgCiMgTW9kZWwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRldGVjdGlvbnMgYW5kIHZlc3NlbHMgKHRoaXMgaXMgdHMgZGF0YSBzbyB0aGlzIGlzIGp1c3QgcXVpY2sgYW5kIGRpcnR5KQoKYGBge3J9CnZlc3NlbF9kZXRlY3Rpb25fZ2xtID0gZ2xtKGRhaWx5X2RldGVjdGlvbnMgfiBkYWlseV92ZXNzZWxzLCBkYXRhID0gZGV0ZWN0aW9uX3Zlc3NlbF9jb3VudHMsIGZhbWlseSA9IHBvaXNzb24pCnByaW50KHN1bW1hcnkodmVzc2VsX2RldGVjdGlvbl9nbG0pKQpwbG90KHZlc3NlbF9kZXRlY3Rpb25fZ2xtKQpgYGAKQ29uY2x1c2lvbjogVGhlcmUgZG9lcyBhcHBlYXIgdG8gYmUgYSBuZWdhdGl2ZSB0cmVuZCB3aGVyZSBpbmNyZWFzZWQgdmVzc2VsIHRyYWZmaWMgcmVzdWx0cyBpbiBmZXdlciBmaXNoLiBTZWUgY29lZmZpY2llbnRzLiBOZWVkIHRvIGRpZyBkZWVwZXIgaWYgdHJ5aW5nIHRvIHB1Ymxpc2ggdGhlc2UgcmVzdWx0cyB0aG91Z2gsIG1vZGVsIHNob3VsZCBiZSBiZXR0ZXIKCiMgUmVzaWRlbmN5IGFuZCBkaXNwZXJzYWwKYGBge3J9CiMjIENhbGN1bGF0ZSByZXNpZGVuY3kKZGV0ZWN0aW9uX3N0YXRzJHJlc2lkZW5jZV9tZXRyaWMgPSBkZXRlY3Rpb25fc3RhdHMkdW5pcXVlX2RheXMgLyBkZXRlY3Rpb25fc3RhdHMkZGF5c19hdF9saWJlcnR5CgojIyBBc3NpZ24gcmVzaWRlbmNlIGNhdGVnb3J5OiBsb3cgPSA8IDMzJSwgbWVkaXVtID0gMzMgLSA2NiwgaGlnaCA9ID49IDY2IChUaW5oYW4gZXQgYWwuIDIwMTQpIC0KZGV0ZWN0aW9uX3N0YXRzJHJlc2lkZW5jZV9jYXRlZ29yeSA9ICdMb3cnCmZvciAoaSBpbiAxOm5yb3coZGV0ZWN0aW9uX3N0YXRzKSl7CiAgaWYgKGRldGVjdGlvbl9zdGF0cyRyZXNpZGVuY2VfbWV0cmljW2ldID49ICgxLzMpKSB7CiAgICBkZXRlY3Rpb25fc3RhdHMkcmVzaWRlbmNlX2NhdGVnb3J5W2ldID0gJ01lZGl1bScKICB9CiAgaWYgKGRldGVjdGlvbl9zdGF0cyRyZXNpZGVuY2VfbWV0cmljW2ldID49ICgyLzMpKSB7CiAgICBkZXRlY3Rpb25fc3RhdHMkcmVzaWRlbmNlX2NhdGVnb3J5W2ldID0gJ0hpZ2gnCiAgfQp9CgojIyBDcmVhdGUgZ3JvdXBlZCBiYXJwbG90IG9mIHJlc2lkZW5jeSBieSBzcGVjaWVzCnJlc2lkZW5jZV9jb3VudHNfYnlfc3BlY2llcyA9IGFnZ3JlZ2F0ZSh0YWdfaWQgfiBzcGVjaWVzICsgcmVzaWRlbmNlX2NhdGVnb3J5LCBkYXRhID0gZGV0ZWN0aW9uX3N0YXRzLCBGVU4gPSBsZW5ndGgpCgpnZ3Bsb3QoZGF0YSA9IHJlc2lkZW5jZV9jb3VudHNfYnlfc3BlY2llcywgbWFwcGluZyA9IGFlcyh4PXNwZWNpZXMsIHk9dGFnX2lkLCBmaWxsPXJlc2lkZW5jZV9jYXRlZ29yeSkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikKYGBgCgpUYWtlYXdheXMgLSBBbGwgNCBvbWlsdXMgd2VyZSBoaWdobHkgcmVzaWRlbnQgYXMgd2VyZSBncmV5IHJlZWYgc2hhcmtzLiBObyBvdGhlciBzcGVjaWVzIGhhdmUgcmVwbGljYXRlcyBzby4uLj8KCiMjIENhbGN1bGF0ZSAzMCBkYXkgbW92aW5nIGF2ZXJhZ2Ugb2YgcmVzaWRlbmN5LCB0aGVuIHBsb3QgYWdhaW5zdCBkYXlzIHNpbmNlIHRhZ2dpbmcKYGBge3J9CiMjIEdldCB0b3RhbCBkYXlzIGluIHRoZSBzdHVkeQp0b3RhbF9kYXlzX2luX3N0dWR5ID0gYXMubnVtZXJpYyhkaWZmLkRhdGUoYyhtaW4obW9sb19kZiRkYXRlKSwgbWF4KG1vbG9fZGYkZGF0ZSkpKSkKCiMjIENyZWF0ZSBhIGRhdGFmcmFtZSB3aGVyZSByb3dzIGFyZSB0YWcgaWQgYW5kIGNvbHVtbnMgYXJlIHN0dWR5IGRhdGUKcHJlc2VudF9hZnRlcl9uX2RheXNfZGYgPSBkYXRhLmZyYW1lKCkKCiMjIERldGVybWluZSBpZiBhIHRhZyB3YXMgZGV0ZWN0ZWQgb24gYSByZWNlaXZlciBuIGRheXMgYWZ0ZXIgdGFnZ2luZwpmb3IgKGkgaW4gMTp1bmlxdWVOKG1vbG9fZGYkdGFnX2lkKSl7CiAgIyMgU3Vic2V0IGRhdGEgZm9yIGluZGl2aWR1YWwgdGFncwogIGluZHZfZGF0YSA9IG1vbG9fZGZbbW9sb19kZiR0YWdfaWQgPT0gdW5pcXVlKG1vbG9fZGYkdGFnX2lkKVtpXSwgXQogICMjIERldGVybWluZSBpZiBhIGZpc2ggd2FzIHByZXNlbnQgbiBkYXlzIGFmdGVyIHRhZ2dpbmcKICBkaWZmdGltZXMgPSByZXAoMCwgbGVuID0gdG90YWxfZGF5c19pbl9zdHVkeSkKICAjIGRldGVybWluZSBkaWZmZXJlbmNlIGluIGRheXMgYmV0d2VlbiBlYWNoIHVuaXF1ZSBkYXkgYSB0YWcgd2FzIGRldGVjdGVkIGFuZCB0aGUgdGFnJ3MgZWFybGllc3QgZGV0ZWN0aW9uLCBmbGlwIHRoZSBjb3JyZXNwb25kaW5nIHZhbHVlIGluIGRpZmZ0aW1lcyBhcnJheSB0byAxCiAgZGV0ZWN0ZWRfZGF0ZXMgPSB1bmlxdWUoaW5kdl9kYXRhJGRhdGUpCiAgZm9yIChqIGluIDE6bGVuZ3RoKGRldGVjdGVkX2RhdGVzKSl7CiAgICBkaWZmdGltZXNbYXMubnVtZXJpYyhkaWZmLkRhdGUoYyhtaW4oaW5kdl9kYXRhJGRhdGUpLCBkZXRlY3RlZF9kYXRlc1tqXSkpKSArIDFdID0gMQogIH0KICBkZl9yb3cgPSBjKHVuaXF1ZShtb2xvX2RmJHRhZ19pZClbaV0sIGRpZmZ0aW1lcykKICBwcmVzZW50X2FmdGVyX25fZGF5c19kZiA9IHJiaW5kKHByZXNlbnRfYWZ0ZXJfbl9kYXlzX2RmLCBkZl9yb3cpCn0KY29sbmFtZXMocHJlc2VudF9hZnRlcl9uX2RheXNfZGYpID0gYygndGFnX2lkJywgYXMuY2hhcmFjdGVyKDE6dG90YWxfZGF5c19pbl9zdHVkeSkpCgojIyBDb252ZXJ0IGZyb20gd2lkZSBmb3JtYXQgdG8gbG9uZyBmb3JtYXQKcHJlc2VudF9hZnRlcl9uX2RheXNfZGZfbG9uZ19kZiA9IG1lbHQocHJlc2VudF9hZnRlcl9uX2RheXNfZGYsIGlkLnZhcnMgPSAndGFnX2lkJywgbWVhc3VyZS52YXJzID0gY29sbmFtZXMocHJlc2VudF9hZnRlcl9uX2RheXNfZGYpWzI6bmNvbChwcmVzZW50X2FmdGVyX25fZGF5c19kZildLCB2YXJpYWJsZS5uYW1lID0gJ2RheScsIHZhbHVlLm5hbWUgPSAnZGV0ZWN0ZWQnKQoKIyMgTWVyZ2Ugd2l0aCBzcGVjaWVzIGRhdGEKcHJlc2VudF9hZnRlcl9uX2RheXNfZGZfbG9uZ19kZiA9IGxlZnRfam9pbih4ID0gcHJlc2VudF9hZnRlcl9uX2RheXNfZGZfbG9uZ19kZiwgeSA9IHRhZ2dpbmdfZGZbICxjKCd0YWdfaWQnLCAnc3BlY2llcycpXSwgYnkgPSAndGFnX2lkJykKIyBSZWNhc3QgdG8gbnVtZXJpYyBiZWNhdXNlIG9mIHRoZSBqb2luIGZ1bmN0aW9uCnByZXNlbnRfYWZ0ZXJfbl9kYXlzX2RmX2xvbmdfZGYkZGV0ZWN0ZWQgPSBhcy5udW1lcmljKHByZXNlbnRfYWZ0ZXJfbl9kYXlzX2RmX2xvbmdfZGYkZGV0ZWN0ZWQpCgojIyBDYWxjdWxhdGUgbnVtYmVyIG9mIGVhY2ggc3BlY2llcyBwcmVzZW50IG4gZGF5cyBhZnRlciB0YWdnaW5nCnNwZWNpZXNfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyA9IGFnZ3JlZ2F0ZShkZXRlY3RlZCB+IHNwZWNpZXMgKyBkYXksIGRhdGEgPSBwcmVzZW50X2FmdGVyX25fZGF5c19kZl9sb25nX2RmLCBGVU4gPSBzdW0pCiAgY29sbmFtZXMoc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nKSA9IGMoJ3NwZWNpZXMnLCAnZGF5JywgJ25fZGV0ZWN0ZWQnKQoKIyMgQ291bnQgdW5pcXVlIHRhZ3MgYnkgc3BlY2llcwppbmRpdmlkdWFsc19wZXJfc3BlY2llcyA9IGFnZ3JlZ2F0ZSh0YWdfaWQgfiBzcGVjaWVzLCBkYXRhID0gcHJlc2VudF9hZnRlcl9uX2RheXNfZGZfbG9uZ19kZiwgRlVOID0gdW5pcXVlTikKY29sbmFtZXMoaW5kaXZpZHVhbHNfcGVyX3NwZWNpZXMpID0gYygnc3BlY2llcycsICduX3RhZ2dlZCcpCgojIyBTdGFuZGFyZGl6ZSBzcGVjaWVzIGxldmVsIGRhaWx5IGNvdW50cyBieSBudW1iZXIgb2YgdGFncyBiZWxvbmdpbmcgdG8gdGhhdCBzcGVjaWVzCnNwZWNpZXNfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyA9IGxlZnRfam9pbih4ID0gc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nLCB5ID0gaW5kaXZpZHVhbHNfcGVyX3NwZWNpZXMsIGJ5ID0gJ3NwZWNpZXMnKQpzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmckcGVyY2VudF9pbmRpdmlkdWFsc19kZXRlY3RlZCA9IHNwZWNpZXNfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyRuX2RldGVjdGVkIC8gc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nJG5fdGFnZ2VkCgojIyBDb252ZXJ0IGRheSBmcm9tIGZhY3RvciB0byBudW1lcmljCnNwZWNpZXNfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyRkYXkgPSBhcy5udW1lcmljKGxldmVscyhzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmckZGF5KVtzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmckZGF5XSkKCiMjIHJlbW92ZSBhbnkgTkEgZGF5cwpzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmcgPSBzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmdbIWlzLm5hKHNwZWNpZXNfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyRkYXkpLCBdCgojIyBDYWxjdWxhdGUgMzAgZGF5IG1vdmluZyBhdmVyYWdlCnNwcF9wcmVzZW5jZV8zMF9kYXlfYXZnID0gZGF0YS5mcmFtZSgpCmZvciAoc3BlY2llcyBpbiB1bmlxdWUoc3BlY2llc19wcmVzZW5jZV9hZnRlcl90YWdnaW5nJHNwZWNpZXMpKXsKICBzcHBfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyA9IHNwZWNpZXNfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZ1tzcGVjaWVzX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmckc3BlY2llcyA9PSBzcGVjaWVzLCBdCiAgbW92aW5nX2F2ZXJhZ2VfMzAgPSBjKCkKICBmb3IgKGkgaW4gMzA6bWF4KHNwcF9wcmVzZW5jZV9hZnRlcl90YWdnaW5nJGRheSkpewogICAgbW92aW5nX2F2ZXJhZ2VfMzAgPSBjKG1vdmluZ19hdmVyYWdlXzMwLCBtZWFuKHNwcF9wcmVzZW5jZV9hZnRlcl90YWdnaW5nJHBlcmNlbnRfaW5kaXZpZHVhbHNfZGV0ZWN0ZWRbc3BwX3ByZXNlbmNlX2FmdGVyX3RhZ2dpbmckZGF5ID49IGktMzAgJiBzcHBfcHJlc2VuY2VfYWZ0ZXJfdGFnZ2luZyRkYXkgPD0gaV0pKQogIH0KICBkZl9yb3cgPSBjKHNwZWNpZXMsIG1vdmluZ19hdmVyYWdlXzMwKQogIHNwcF9wcmVzZW5jZV8zMF9kYXlfYXZnID0gcmJpbmQoc3BwX3ByZXNlbmNlXzMwX2RheV9hdmcsIGRmX3JvdykKfQpjb2xuYW1lcyhzcHBfcHJlc2VuY2VfMzBfZGF5X2F2ZykgPSBjKCdzcGVjaWVzJywgYXMuY2hhcmFjdGVyKDE6KG5jb2woc3BwX3ByZXNlbmNlXzMwX2RheV9hdmcpLTEpKSkKCiMjIENvbnZlcnQgZnJvbSB3aWRlIGZvcm1hdCB0byBsb25nIGZvcm1hdApzcHBfcHJlc2VuY2VfMzBfZGF5X2F2Z19sb25nX2RmID0gbWVsdChzcHBfcHJlc2VuY2VfMzBfZGF5X2F2ZywgaWQudmFycyA9ICdzcGVjaWVzJywgbWVhc3VyZS52YXJzID0gY29sbmFtZXMoc3BwX3ByZXNlbmNlXzMwX2RheV9hdmcpWzI6bmNvbChzcHBfcHJlc2VuY2VfMzBfZGF5X2F2ZyldLCB2YXJpYWJsZS5uYW1lID0gJ2RheScsIHZhbHVlLm5hbWUgPSAncGVyY2VudF9pbmRpdmlkdWFsc19kZXRlY3RlZCcpCgojIENvbnZlcnQgcGVyY2VudF9pbmRpdmlkdWFsc19kZXRlY3RlZCBhbmQgZGF0ZQpzcHBfcHJlc2VuY2VfMzBfZGF5X2F2Z19sb25nX2RmJHBlcmNlbnRfaW5kaXZpZHVhbHNfZGV0ZWN0ZWQgPSBhcy5udW1lcmljKHNwcF9wcmVzZW5jZV8zMF9kYXlfYXZnX2xvbmdfZGYkcGVyY2VudF9pbmRpdmlkdWFsc19kZXRlY3RlZCkKCnNwcF9wcmVzZW5jZV8zMF9kYXlfYXZnX2xvbmdfZGYkZGF5ID0gYXMubnVtZXJpYyhsZXZlbHMoc3BwX3ByZXNlbmNlXzMwX2RheV9hdmdfbG9uZ19kZiRkYXkpW3NwcF9wcmVzZW5jZV8zMF9kYXlfYXZnX2xvbmdfZGYkZGF5XSkKCgojIyBHZW5lcmF0ZSBsaW5lIHBsb3QKcHJlc2VudF9hZnRlcl90YWdnaW5nX3Bsb3QgPSBnZ3Bsb3Qoc3BwX3ByZXNlbmNlXzMwX2RheV9hdmdfbG9uZ19kZiwgbWFwcGluZyA9IGFlcyh4ID0gZGF5LCB5ID0gcGVyY2VudF9pbmRpdmlkdWFsc19kZXRlY3RlZCwgY29sb3IgPSBzcGVjaWVzKSkgKyAKICBnZW9tX2xpbmUoKSArIAogIGxhYnMoeCA9ICdOdW1iZXIgb2YgZGF5cycsIHkgPSAnUHJvcG9ydGlvbiBwcmVzZW50JykgKwogIGdnc2F2ZShmaWxlbmFtZSA9ICdQcm9wb3J0aW9uIG9mIHRhZ3MgcHJlc2VudCBhZnRlciB0YWdnaW5nLnBkZicsIHBhdGggPSBmaWd1cmVfZGlyZWN0b3J5KQoKcHJpbnQocHJlc2VudF9hZnRlcl90YWdnaW5nX3Bsb3QpCmBgYAoKIyBTdGF0aXN0aWNhbCBhbmFseXNpcwoKQ2FsY3VsYXRlIG1lYW4gcmVzaWRlbmN5IGJ5IHNwcCAoaXJyZWdhcmRsZXNzIG9mIHRpbWUpLCB0aGVuIEFOT1ZBIGJ5IHNwcApVc2UgVHVrZXkncyBIU0QgdG8gZGV0ZXJtaW5lIHNpZ25pZmljYW5jZQpgYGB7cn0KIyMgQU5PVkEgbW9kZWwgZm9yIHJlc2lkZW5jeSBtZXRyaWMgYnkgc3BlY2llcwpyZXNpZGVuY2VfYnlfc3BlY2llc19hbm92YSA9IGFvdihyZXNpZGVuY2VfbWV0cmljIH4gc3BlY2llcywgZGF0YT1kZXRlY3Rpb25fc3RhdHMpCnN1bW1hcnkocmVzaWRlbmNlX2J5X3NwZWNpZXNfYW5vdmEpCgojIyBUdWtleSdzIEhvbmVzdGx5IFNpZ25pZmljYW50IERpZmZlcmVuY2VzIGJldHdlZW4gc3BlY2llcwpUdWtleUhTRChyZXNpZGVuY2VfYnlfc3BlY2llc19hbm92YSkKYGBgCgpHTE0gY29tcGFyaW5nIHJlc2lkZW5jeSB0aW1lIGJ5IHNwcCBpbmRlcGVuZGVudCB2YXIgKHRpbWUgYXQgbGliZXJ0eSkgZGVwZW5kZW50IChyZXNpZGVuY3kgaW5kZXgpCmBgYHtyfQojIyBGaXQgYmlub21pYWwgR0xNIHRvIGF2ZXJhZ2UgcmVzaWRlbmN5IG1ldHJpYyBkYXRhIChwcm9wb3J0aW9uYWwgYmV0d2VlbiAwLTEpCnNwZWNpZXNfZ2xtID0gZ2xtKHJlc2lkZW5jZV9tZXRyaWMgfiAgc3BlY2llcyAqIGRheXNfYXRfbGliZXJ0eSwgZGF0YSA9IGRldGVjdGlvbl9zdGF0cywgZmFtaWx5ID0gYmlub21pYWwobG9naXQpKQpzdW1tYXJ5KHNwZWNpZXNfZ2xtKQpgYGAKTm8gU2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgZm91bmQgCgoKIyMgR0xNIGNvbXBhcmluZyB0aW1lIGluIGNyYXRlciB0byB2ZXNzZWwgdHJhZmZpYyAtIFN0aWxsIHRvIGNvbWUKYGBge3J9CgpgYGA=